Units

Newton OS 2.0 provides the ability for packages to share informations by
exporting or importing units. Units are similar to shared libraries in
other systems.

A unit provides a collection of NS objects (unit members). Units are
identified by a name, major version number, and minor version number.
Any frame part can export or import zero or more units.

A unit must be declared, using DeclareUnit, before it's used (imported
or exported). See the docs on DeclareUnit below for details.

To export a unit, call DefineUnit and specify the NS objects that are
exported.

To import from a unit, simply reference its members using UnitReference
(or UR for short).


Unit Usage Notes:

- Units can also be used to share objects among parts within a single
package. This avoids the need to resort to global variables or similar
undesireable techniques.

- A part can export multiple units. To achieve some degree of privacy
you can partition your objects into private and public units. Privacy
is achieved by not providing the declaration for a unit.

- References to units are resolved dynamically whenever a package is
activated or deactivated. For example, a package can be loaded before
the package providing the units it imports is loaded. There will be no
problem as long as the provider is loaded prior to actually using the
imported members.

Conversely, it's possible for the provider to be deactived while its
units are in us. The part frame methods, RemovalApproval and
ImportDisabled, provide a way to deal with this situation.

Robust code should ensure the units it imports are available before
attempting to use their members. It should also gracefully handle units
being removed while in use. See the sample code "MooUnit" for a simple
example.

===== ===== ===== ===== ===== ===== ===== ===== ===== ===== ===== =====

Unit Build-Time Functions:

These functions are available in NTK at build-time only.

===== ===== =====

DeclareUnit(unitName, majorVersion, minorVersion, memberIndexes)
    unitName - symbol - name of the unit
    majorVersion - integer - major version number of the unit
    minorVersion - integer - minor version number of the unit
    memberIndexes - frame - unit member name/index pairs (slot/value) 
    return value - unspecified

A unit musted be declared by DeclareUnit before it's used (imported or
exported). The declaration maps the member names to their indexes. A
typical declaration looks like:

    DeclareUnit('|FastFourierTransforms:MathMagiks|, 1, 0, {
        ProtoGraph:     0,
        ProtoDataSet:   1,
    });

Typically, the declarations for a unit are provided in a file, such as
"FastFourierTransforms.unit", that is added to an NTK project (a la .h
files in C).

When resolving imports, the name and major version specified by the
importer and exporter must match exactly. The minor version does not
have to match exactly. If there are units differing only in minor
version, the one with the largest minor version is used.

Typically, the first version of a unit will have major version 1 and
minor version 0. As bug fixes releases are made, the minor version is
incremented. If a major (incompatible) change is made, then the major
version number is incremented.

Note: When a unit is modified, the indexes of the existing members must
remain the same. I.e. adding new members is safe as long as the indexes
of the existing members don't change. If you change an member's index
it will be incompatible with any existing clients (until they're
recompiled with the new declaration)

===== ===== =====

DefineUnit(unitName, members)
    unitName - symbol - name of the unit
    members - frame - unit member name/value pairs (slot/value)
    return value - unspecified

DefineUnit exports a unit and specifies the value of each member.
Immediates and symbols are not allowed as member values. A typical
definition looks like:

    DefineUnit('|FastFourierTransforms:MathMagiks|, {
        ProtoGraph:     GetLayout("foo.layout"),
        ProtoDataSet:   { ... },
    });

A unit must be declared before it's defined. The declaration used when
exporting a unit with n members must contain n slots with indexes
0..n-1. The definition must specify a value for EVERY declared member.

===== ===== =====

UnitReference(unitName, memberName)
    or
UR(unitName, memberName)
    unitName - symbol - name of a unit
    memberName - symbol - name of a member of unit
    return value - a reference to the specified member

To use a unit member call UnitReference (UR for short) with the unit and
member name.

The unit name 'ROM can be used to refer to obects in the base ROM. E.g.
UR('ROM, 'ProtoLabelInputLine).

Note: references to objects in the base ROM are sometimes called "magic
pointers" and have traditionally been provided in NTK by constants like
ProtoLabelInputLine or ROM_SystemSoupName.

In Newton OS 2.0, there may also be packages in the ROM. These ROM
packages may provide units. Their members are referenced just like any
other unit, using UR, the unitName, and the memberName. This is the
mechanism by which licensees can provide product specific
functionality.

===== ===== =====

AliasUnit(alias, unitName)
    alias - symbol - alternate name for unit
    unitName - symbol - name of a unit
    return value - unspecified
    
AliasUnit provides a way to specify an alternate name for a unit. Since
unit names must be unigue, they tend to be long and cumbersome. E.g.
you might use:

    AliasUnit('FFT, '|FastFourierTransforms:MathMagiks|);

so that you could write:
    
    local data := UR('FFT, 'ProtoDataSet):New(points);

instead of:

    local data := UR('|FastFourierTransforms:MathMagiks|, 'ProtoDataSet):New(points);

===== ===== =====

AliasUnitSubset(alias, unitName, memberNames)
    alias - symbol - alternate name for unit
    unitName - symbol - name of a unit
    memberNames - array of symbols - list of unit member names
    return value - unspecified

AliasUnitSubset is similar to AliasUnit, except that it additionally
specifies a subset of the units members which can be used. This helps
restrict code to using only certain members of a unit.

===== ===== ===== ===== ===== ===== ===== ===== ===== ===== ===== =====

Unit Part Frame Methods:

These methods can be (optionally) defined in a part frame to handle
units becoming unavailable.

===== ===== =====

RemovalApproval(unitName, majorVersion, minorVersion)
    unitName - symbol - name of the unit
    majorVersion - integer - major version number of the unit
    minorVersion - integer - minor version number of the unit
    return value - nil or string

This message is sent to a part frame when an imported unit is about to
be deactivated. It may a return a string to be shown to the user as a
warning about the consequences of deactivating the package in use. For
example:

"This operation will cause your connection to fooWorld to be dropped."

Note: do not assume the user is removing the package. Other operations
such as moving a package between stores also cause package
deactivation.

This message is only a warning. The user may decide to proceed and
suffer the consequences. If the user proceeds, the ImportDisabled
message (see below) will be sent.

If the removing the unit is not a problem (e.g. your app is closed) then
RemovalApproval can return nil and the user will not be bothered.

===== ===== =====

ImportDisabled(unitName, majorVersion, minorVersion)
    unitName - symbol - name of the unit
    majorVersion - integer - major version number of the unit
    minorVersion - integer - minor version number of the unit
    return value - unspecified

This message is sent to a part frame after an imported unit has been
deactivated. The part should deal with the situation as gracefully as
possible. E.g. Use alternative data or put up a Notify and/or close
your app.

===== ===== ===== ===== ===== ===== ===== ===== ===== ===== ===== =====

Unit Related Glue Functions:

These functions are available in the Newton 2.0 Platform file.
                
===== ===== =====

MissingImports(pkgRef)
    return value - nil or an array of frames (see below)
    glue name - kMissingImportsFunc
    
MissingImports lists the units used by the specified package that are
not currently available. MissingImports returns either nil, indicating
there are no missing units, or an an array of frames of the form:

    {
         name: symbol  - name of unit desired
        major: integer - major version number
        minor: integer - minor version number

        <other slots undocumented>
    }

===== ===== =====
